﻿using System;
using System.Linq;


namespace RobotRules.Cache
{
    /// <summary>
    /// A simple naive memory cache
    /// </summary>
#if DEBUG
    public class MemoryCache : ICache
#else
    internal class MemoryCache : ICache
#endif
    {
        private bool _disposed;
        private readonly DateTimeOffset _cacheTimeout;
        private readonly System.Runtime.Caching.MemoryCache _memoryCache;

        /// <summary>
        /// Initialize a new instance of <see cref="MemoryCache"/>
        /// </summary>
        public MemoryCache()
            :this(TimeSpan.FromSeconds(60))
        {
            
        }

        /// <summary>
        /// Initialize a new instance of <see cref="MemoryCache"/>
        /// </summary>
        /// <param name="cacheTimeout"> The fixed date and time at which the cache entry will expire.</param>
        public MemoryCache(TimeSpan cacheTimeout)
        {
            _memoryCache = new System.Runtime.Caching.MemoryCache("RobotRules");
            _cacheTimeout = DateTimeOffset.Now.Add(cacheTimeout);
        }
        
        /// <summary>
        /// Cache value
        /// </summary>
        private struct MemoryCacheValue
        {
            /// <summary>
            /// Robot file content
            /// </summary>
            public string[] Value { get; private set; }
            /// <summary>
            /// Add date
            /// </summary>
            public DateTime Date { get; private set; }

            public MemoryCacheValue(string[] value)
                : this()
            {
                Value = value;
                Date = DateTime.UtcNow;
            }
        }

        /// <summary>
        /// Add a new document to the cache
        /// </summary>
        /// <param name="url">Robot file uri</param>
        /// <param name="content">File content</param>
        /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception>
        /// <exception cref="ArgumentException"><paramref name="content"/> is null or empty</exception>
        public void Add(Uri url, string[] content)
        {
            _memoryCache.Add(url.AbsoluteUri, new MemoryCacheValue(content), _cacheTimeout);
        }

        /// <summary>
        /// Delete a robot file
        /// </summary>
        /// <param name="url">Robot file uri</param>
        /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception>
        public void Delete(Uri url)
        {
            _memoryCache.Remove(url.AbsoluteUri);
        }

        /// <summary>
        /// Get the date when the file has been cached
        /// </summary>
        /// <param name="url">Robot file uri</param>
        /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception>
        /// <returns>Date</returns>
        public DateTime? GetDate(Uri url)
        {
            var value = _memoryCache.Get(url.AbsoluteUri);
            if (value != null)
            {
                return ((MemoryCacheValue) value).Date;
            }

            return null;
        }

        /// <summary>
        /// Update a robot file
        /// </summary>
        /// <param name="url">Robot file uri</param>
        /// <param name="content">File content</param>
        /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception>
        /// <exception cref="ArgumentException"><paramref name="content"/> is null or empty</exception>
        public void Update(Uri url, string[] content)
        {
            _memoryCache.Set(url.AbsoluteUri, new MemoryCacheValue(content), new System.Runtime.Caching.CacheItemPolicy { AbsoluteExpiration = _cacheTimeout });
        }

        /// <summary>
        /// Check if a file exists
        /// </summary>
        /// <param name="url">Robot file uri</param>
        /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception>
        /// <returns><c>True</c> if the file exists, <c>False</c> instead</returns>
        public bool Exists(Uri url)
        {
            return _memoryCache.Contains(url.AbsoluteUri);
        }

        /// <summary>
        /// Get robot file content
        /// </summary>
        /// <param name="url">Robot file uri</param>
        /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception>
        /// <returns>File content</returns>
        public string[] GetFile(Uri url)
        {
            var value = _memoryCache.Get(url.AbsoluteUri);
            if (value != null)
            {
                return ((MemoryCacheValue) value).Value;
            }

            return null;
        }

        /// <summary>
        /// Clear the cache
        /// </summary>
        public void Clear()
        {
            lock (this)
            {
                var cacheKeys = _memoryCache.Select(kvp => kvp.Key).ToList();
                foreach (var cacheKey in cacheKeys)
                {
                    _memoryCache.Remove(cacheKey);
                }
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        

        protected virtual void Dispose(bool dispose)
        {
            if (dispose && !_disposed)
            {
                _disposed = true;

                _memoryCache.Dispose();
            }
        }
    }
}
